package com.gobang.service.impl;

import com.gobang.common.exception.GameException;
import com.gobang.common.util.MessageUtils;
import com.gobang.domain.dto.MoveResponseDTO;
import com.gobang.domain.dto.ScoreUpdateRequestDTO;
import com.gobang.domain.dto.SurrenderRequestDTO;
import com.gobang.domain.entity.GameRoom;
import com.gobang.domain.enums.PlayerColor;
import com.gobang.domain.message.GameOverMessage;
import com.gobang.domain.message.GameStateUpdateMessage;
import com.gobang.domain.vo.GameRoomVO;
import com.gobang.domain.vo.SurrenderResponseVO;
import com.gobang.domain.vo.UserInfoVO;
import com.gobang.mapper.GameRoomMapper;
import com.gobang.service.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.util.Map;

/**
 * 游戏管理业务服务实现类
 * 处理游戏核心业务逻辑
 * 
 * @author System
 * @since 1.0.0
 */
@Service
@RequiredArgsConstructor
@Transactional
public class NewGameServiceImpl implements NewGameService {

    private static final Logger log = LoggerFactory.getLogger(NewGameServiceImpl.class);

    private final GameRoomMapper gameRoomMapper;
    private final UserService userService;
    private final ScoreService scoreService;
    private final GameEngine gameEngine;
    private final BoardManager boardManager;
    private final GameAlgorithm gameAlgorithm;
    private final StringRedisTemplate redisTemplate;
    private final ObjectMapper objectMapper;
    private final ConnectionManagerService connectionManagerService;

    @Override
    public GameRoomVO getRoomInfo(String roomId, Long userId) {
        log.info("获取游戏房间信息，roomId: {}, userId: {}", roomId, userId);
        
        // 从Redis获取房间状态
        String roomKey = "room:" + roomId;
        Map<Object, Object> roomData = redisTemplate.opsForHash().entries(roomKey);
        
        if (roomData.isEmpty()) {
            // Redis中没有数据，从MySQL查询
            GameRoom gameRoom = gameRoomMapper.findByRoomId(roomId);
            if (gameRoom == null) {
                throw GameException.gameRoomNotFound();
            }
            
            // 检查用户权限
            if (!gameRoom.getBlackPlayerId().equals(userId) && 
                !gameRoom.getWhitePlayerId().equals(userId)) {
                throw GameException.gamePlayerNotInRoom();
            }
            
            // 重建Redis缓存
            rebuildRoomCache(gameRoom);
            roomData = redisTemplate.opsForHash().entries(roomKey);
        }
        
        // 转换为VO对象
        return convertToGameRoomVO(roomData, roomId);
    }

    @Override
    public MoveResponseDTO makeMove(String roomId, Long userId, int row, int col) {
        log.info("处理落子请求，roomId: {}, userId: {}, 位置: ({}, {})", roomId, userId, row, col);
        
        try {
            // 获取房间状态
            String roomKey = "room:" + roomId;
            Map<Object, Object> roomData = redisTemplate.opsForHash().entries(roomKey);
            
            if (roomData.isEmpty()) {
                throw GameException.gameRoomNotFound();
            }
            
            // 验证游戏状态
            String gameStatus = (String) roomData.get("game_status");
            if (!"PLAYING".equals(gameStatus)) {
                return MoveResponseDTO.builder()
                    .roomId(roomId)
                    .success(false)
                    .errorMessage("游戏未在进行中")
                    .build();
            }
            
            // 检查是否轮到该玩家
            if (!gameEngine.isPlayerTurn(roomId, userId)) {
                return MoveResponseDTO.builder()
                    .roomId(roomId)
                    .success(false)
                    .errorMessage("当前不是您的回合")
                    .nextTurn(gameEngine.getCurrentTurn(roomId).getCode())
                    .build();
            }
            
            // 获取棋盘状态
            String boardStateJson = (String) roomData.get("board_state");
            int[][] board = parseBoardFromJson(boardStateJson);
            
            // 获取玩家颜色
            PlayerColor playerColor = gameEngine.getPlayerColor(roomId, userId);
            int colorValue = playerColor.getValue();
            
            // 验证落子
            if (!boardManager.validateMove(board, row, col, colorValue, colorValue)) {
                return MoveResponseDTO.builder()
                    .roomId(roomId)
                    .success(false)
                    .errorMessage("无效的落子位置")
                    .nextTurn(playerColor.getCode())
                    .build();
            }
            
            // 执行落子
            boardManager.placeStone(board, row, col, colorValue);
            
            // 检查胜负
            boolean isWin = gameAlgorithm.checkWin(board, row, col, colorValue);
            
            // 更新Redis状态
            updateRoomStateAfterMove(roomId, board, isWin, userId, playerColor);
            
            // 获取用户信息
            UserInfoVO user = userService.getUserById(userId);
            
            // 构建落子信息
            MoveResponseDTO.MoveInfo moveInfo = MoveResponseDTO.MoveInfo.builder()
                .row(row)
                .col(col)
                .color(playerColor.getCode())
                .player(MoveResponseDTO.PlayerInfo.builder()
                    .id(userId)
                    .username(user.getUsername())
                    .build())
                .build();
            
            // 构建响应
            MoveResponseDTO response = MoveResponseDTO.builder()
                .roomId(roomId)
                .success(true)
                .move(moveInfo)
                .gameStatus(isWin ? "FINISHED" : "PLAYING")
                .isGameOver(isWin)
                .winnerId(isWin ? userId : null)
                .nextTurn(isWin ? null : playerColor.getOpponent().getCode())
                .build();
            
            // 如果游戏结束，处理结束逻辑
            if (isWin) {
                handleGameEnd(roomId, userId, "FIVE_IN_ROW");
            } else {
                // 切换回合
                gameEngine.switchTurn(roomId);
                // 增加步数
                gameRoomMapper.incrementTotalMoves(roomId);
            }
            
            log.info("落子处理成功，roomId: {}, userId: {}, 游戏结束: {}", roomId, userId, isWin);
            return response;
            
        } catch (Exception e) {
            log.error("处理落子请求失败", e);
            return MoveResponseDTO.builder()
                .roomId(roomId)
                .success(false)
                .errorMessage("系统错误：" + e.getMessage())
                .build();
        }
    }


//    @Override
//    public void handleGameOver(String roomId, Long winnerId) {
//        log.info("处理游戏结束，roomId: {}, winnerId: {}", roomId, winnerId);
//        handleGameEnd(roomId, winnerId, "FIVE_IN_ROW");
//    }

    @Override
    public GameStateUpdateMessage getGameState(String roomId, Long userId) {
        log.debug("获取游戏状态，roomId: {}, userId: {}", roomId, userId);
        
        try {
            String roomKey = "room:" + roomId;
            Map<Object, Object> roomData = redisTemplate.opsForHash().entries(roomKey);
            
            if (roomData.isEmpty()) {
                throw GameException.gameRoomNotFound();
            }
            
            // 检查用户权限
            Long blackPlayerId = Long.valueOf((String) roomData.get("black_player_id"));
            Long whitePlayerId = Long.valueOf((String) roomData.get("white_player_id"));
            
            if (!userId.equals(blackPlayerId) && !userId.equals(whitePlayerId)) {
                throw GameException.gamePlayerNotInRoom();
            }
            
            String currentTurn = (String) roomData.get("current_turn");
            String gameStatus = (String) roomData.get("game_status");
            Integer moveCount = Integer.valueOf((String) roomData.get("move_count"));
            String boardStateJson = (String) roomData.get("board_state");
            int[][] board = boardManager.parseBoardFromJson(boardStateJson);

            // 构建游戏状态数据
            return MessageUtils.createGameStateUpdateMessage(
                    roomId, currentTurn, moveCount, gameStatus,
                    null,null,null, board);

//            GameStateUpdateMessage.GameStateData stateData =
//                new GameStateUpdateMessage.GameStateData(
//                    roomId, currentTurn, null, moveCount, gameStatus);
//
//            return new GameStateUpdateMessage(stateData);

        } catch (Exception e) {
            log.error("获取游戏状态失败", e);
            throw GameException.gameStateSyncError();
        }
    }

    /**
     * 处理游戏结束
     */
    @Override
    public void handleGameEnd(String roomId, Long winnerId, String endReason) {
        log.info("处理游戏结束，roomId: {}, winnerId: {}, reason: {}", roomId, winnerId, endReason);
        
        try {
            // 更新Redis游戏状态
            String roomKey = "room:" + roomId;
            redisTemplate.opsForHash().put(roomKey, "game_status", "FINISHED");
            redisTemplate.opsForHash().put(roomKey, "winner_id", winnerId.toString());
            redisTemplate.opsForHash().put(roomKey, "end_reason", endReason);
            redisTemplate.expire(roomKey, Duration.ofHours(1));
            
            // 获取房间信息
            Map<Object, Object> roomData = redisTemplate.opsForHash().entries(roomKey);
            Long blackPlayerId = Long.valueOf((String) roomData.get("black_player_id"));
            Long whitePlayerId = Long.valueOf((String) roomData.get("white_player_id"));
            Long loserId = winnerId.equals(blackPlayerId) ? whitePlayerId : blackPlayerId;
            
            // 通知积分管理模块更新积分(更新两人的积分)
            ScoreUpdateRequestDTO winnerRequest = new ScoreUpdateRequestDTO();
            winnerRequest.setUserId(winnerId);
            winnerRequest.setOpponentId(loserId);
            winnerRequest.setRoomId(roomId);
            winnerRequest.setGameResult("WIN");
            winnerRequest.setScoreChange(5);
            scoreService.updateUserScore(winnerRequest);
            ScoreUpdateRequestDTO loserRequest = new ScoreUpdateRequestDTO();
            loserRequest.setUserId(loserId);
            loserRequest.setOpponentId(winnerId);
            loserRequest.setRoomId(roomId);
            loserRequest.setGameResult("LOSE");
            loserRequest.setScoreChange(-3);
            scoreService.updateUserScore(loserRequest);
            // scoreService.updateGameResult(winnerId, loserId, roomId);
            
            // 更新MySQL游戏记录
            gameRoomMapper.updateGameResult(roomId, winnerId, "FINISHED", endReason);


            // 通知匹配系统游戏结束
            // matchSystemService.notifyGameEnd(blackPlayerId, whitePlayerId);
            connectionManagerService.updateUserStatus(winnerId, "online");
            connectionManagerService.updateUserStatus(loserId, "online");
            
            log.info("游戏结束处理完成，roomId: {}", roomId);
            
        } catch (Exception e) {
            log.error("处理游戏结束失败", e);
            throw GameException.gameStateSyncError();
        }
    }

    /**
     * 更新房间状态（落子后）
     */
    private void updateRoomStateAfterMove(String roomId, int[][] board, boolean isWin, 
                                        Long userId, PlayerColor playerColor) {
        String roomKey = "room:" + roomId;
        String boardJson = convertBoardToJson(board);
        
        redisTemplate.opsForHash().put(roomKey, "board_state", boardJson);
        redisTemplate.opsForHash().put(roomKey, "last_move_time", String.valueOf(System.currentTimeMillis()));
        
        if (isWin) {
            redisTemplate.opsForHash().put(roomKey, "game_status", "FINISHED");
            redisTemplate.opsForHash().put(roomKey, "winner_id", userId.toString());
        }
        
        // 增加落子数
        String moveCountStr = (String) redisTemplate.opsForHash().get(roomKey, "move_count");
        int moveCount = Integer.parseInt(moveCountStr) + 1;
        redisTemplate.opsForHash().put(roomKey, "move_count", String.valueOf(moveCount));
    }

    /**
     * 重建Redis缓存
     */
    private void rebuildRoomCache(GameRoom gameRoom) {
        log.debug("重建房间缓存，roomId: {}", gameRoom.getId());
        
        String roomKey = "room:" + gameRoom.getId();
        
        redisTemplate.opsForHash().put(roomKey, "black_player_id", gameRoom.getBlackPlayerId().toString());
        redisTemplate.opsForHash().put(roomKey, "white_player_id", gameRoom.getWhitePlayerId().toString());
        redisTemplate.opsForHash().put(roomKey, "current_turn", gameRoom.getCurrentTurn());
        redisTemplate.opsForHash().put(roomKey, "game_status", gameRoom.getGameStatus());
        redisTemplate.opsForHash().put(roomKey, "move_count", gameRoom.getTotalMoves().toString());
        
        if (gameRoom.getWinnerId() != null) {
            redisTemplate.opsForHash().put(roomKey, "winner_id", gameRoom.getWinnerId().toString());
        }
        
        // 初始化空棋盘（实际项目中应该从某处恢复棋盘状态）
        int[][] emptyBoard = gameAlgorithm.initializeBoard();
        String boardJson = convertBoardToJson(emptyBoard);
        redisTemplate.opsForHash().put(roomKey, "board_state", boardJson);
    }

    /**
     * 转换为游戏房间VO
     */
    private GameRoomVO convertToGameRoomVO(Map<Object, Object> roomData, String roomId) {
        try {
            Long blackPlayerId = Long.valueOf((String) roomData.get("black_player_id"));
            Long whitePlayerId = Long.valueOf((String) roomData.get("white_player_id"));
            
            // 获取玩家信息
            UserInfoVO blackPlayer = userService.getUserById(blackPlayerId);
            UserInfoVO whitePlayer = userService.getUserById(whitePlayerId);
            
            // 获取棋盘状态
            String boardStateJson = (String) roomData.get("board_state");
            int[][] boardState = parseBoardFromJson(boardStateJson);

            Long winnerId = roomData.get("winner_id") != null ?
                    Long.valueOf((String) roomData.get("winner_id")) : null;

            String winnerName = winnerId == null ? null : (winnerId.equals(blackPlayerId)
                    ? blackPlayer.getUsername() : whitePlayer.getUsername());

            return GameRoomVO.builder()
                .roomId(roomId)
                .blackPlayer(GameRoomVO.PlayerInfo.builder()
                    .id(blackPlayerId)
                    .username(blackPlayer.getUsername())
                    .currentScore(blackPlayer.getCurrentScore())
                    .color("BLACK")
                    .build())
                .whitePlayer(GameRoomVO.PlayerInfo.builder()
                    .id(whitePlayerId)
                    .username(whitePlayer.getUsername())
                    .currentScore(whitePlayer.getCurrentScore())
                    .color("WHITE")
                    .build())
                .currentTurn((String) roomData.get("current_turn"))
                .gameStatus((String) roomData.get("game_status"))
                .boardState(boardState)
                .moveCount(Integer.valueOf((String) roomData.get("move_count")))
                .winnerId(winnerId)
                .winnerUsername(winnerName)
                .endReason((String) roomData.get("end_reason"))
                .build();
                
        } catch (Exception e) {
            log.error("转换游戏房间VO失败", e);
            throw GameException.gameStateSyncError();
        }
    }

    /**
     * 从JSON解析棋盘状态
     */
    private int[][] parseBoardFromJson(String boardJson) {
        try {
            return objectMapper.readValue(boardJson, int[][].class);
        } catch (JsonProcessingException e) {
            log.error("解析棋盘状态失败: {}", boardJson, e);
            throw GameException.gameBoardStateError();
        }
    }

    /**
     * 将棋盘状态转换为JSON
     */
    private String convertBoardToJson(int[][] board) {
        try {
            return objectMapper.writeValueAsString(board);
        } catch (JsonProcessingException e) {
            log.error("转换棋盘状态失败", e);
            throw GameException.gameBoardStateError();
        }
    }
} 